annex.assistant.allowunlocked
authorJoey Hess <joeyh@joeyh.name>
Tue, 16 Sep 2025 18:58:26 +0000 (14:58 -0400)
committerJoey Hess <joeyh@joeyh.name>
Tue, 16 Sep 2025 18:58:26 +0000 (14:58 -0400)
Sponsored-by: the NIH-funded NICEMAN (ReproNim TR&D3) project
Assistant/Threads/Committer.hs
CHANGELOG
Types/GitConfig.hs
doc/git-annex.mdwn
doc/todo/allow_configuring_assistant_to_add_files_locked.mdwn

index 6ffc9eb0e14f8bad62d4db9ae6da73e8080505f3..53663e8b5b02e152c5ec50ff82f4f5b5cc08871d 100644 (file)
@@ -62,6 +62,11 @@ commitThread = namedThread "Committer" $ do
                fmap Seconds . annexDelayAdd <$> Annex.getGitConfig
        largefilematcher <- liftAnnex largeFilesMatcher
        annexdotfiles <- liftAnnex $ getGitConfigVal annexDotFiles
+       addunlockedmatcher <- liftAnnex $
+               ifM (annexSupportUnlocked <$> Annex.getGitConfig)
+                       ( Just <$> addUnlockedMatcher
+                       , return Nothing
+                       )
        msg <- liftAnnex Command.Sync.commitMsg
        lockdowndir <- liftAnnex $ fromRepo gitAnnexTmpWatcherDir
        liftAnnex $ do
@@ -70,7 +75,7 @@ commitThread = namedThread "Committer" $ do
                void $ liftIO $ tryIO $ removeDirectoryRecursive lockdowndir
                void $ createAnnexDirectory lockdowndir
        waitChangeTime $ \(changes, time) -> do
-               readychanges <- handleAdds lockdowndir havelsof largefilematcher annexdotfiles delayadd $
+               readychanges <- handleAdds lockdowndir havelsof largefilematcher annexdotfiles addunlockedmatcher delayadd $
                        simplifyChanges changes
                if shouldCommit False time (length readychanges) readychanges
                        then do
@@ -275,8 +280,8 @@ commitStaged msg = do
  - Any pending adds that are not ready yet are put back into the ChangeChan,
  - where they will be retried later.
  -}
-handleAdds :: OsPath -> Bool -> GetFileMatcher -> Bool -> Maybe Seconds -> [Change] -> Assistant [Change]
-handleAdds lockdowndir havelsof largefilematcher annexdotfiles delayadd cs = returnWhen (null incomplete) $ do
+handleAdds :: OsPath -> Bool -> GetFileMatcher -> Bool -> Maybe AddUnlockedMatcher -> Maybe Seconds -> [Change] -> Assistant [Change]
+handleAdds lockdowndir havelsof largefilematcher annexdotfiles addunlockedmatcher delayadd cs = returnWhen (null incomplete) $ do
        let (pending, inprocess) = partition isPendingAddChange incomplete
        let lockdownconfig = LockDownConfig
                { lockingFile = False
@@ -340,9 +345,9 @@ handleAdds lockdowndir havelsof largefilematcher annexdotfiles delayadd cs = ret
                        Command.Add.addFile Command.Add.Small f
                                =<< liftIO (R.getSymbolicLinkStatus (fromOsPath f))
 
-       {- Avoid overhead of re-injesting a renamed unlocked file, by
-        - examining the other Changes to see if a removed file has the
-        - same InodeCache as the new file. If so, we can just update
+       {- When adding the file unlocked, avoid overhead of re-injesting a renamed
+        - unlocked file, by examining the other Changes to see if a removed
+        - file has the same InodeCache as the new file. If so, we can just update
         - bookkeeping, and stage the file in git.
         -}
        addannexed :: [Change] -> Assistant [Maybe Change]
@@ -357,18 +362,36 @@ handleAdds lockdowndir havelsof largefilematcher annexdotfiles delayadd cs = ret
                        , checkWritePerms = True
                        }
                if M.null m
-                       then forM toadd (addannexed' cfg)
+                       then forM toadd $ \c -> do
+                               mcache <- liftIO $ genInodeCache (changeFile c) delta
+                               addunlocked <- checkaddunlocked c
+                               addannexed' cfg c addunlocked mcache
                        else forM toadd $ \c -> do
                                mcache <- liftIO $ genInodeCache (changeFile c) delta
-                               case mcache of
-                                       Nothing -> addannexed' cfg c
-                                       Just cache ->
-                                               case M.lookup (inodeCacheToKey ct cache) m of
-                                                       Nothing -> addannexed' cfg c
-                                                       Just k -> fastadd c k
-
-       addannexed' :: LockDownConfig -> Change -> Assistant (Maybe Change)
-       addannexed' lockdownconfig change@(InProcessAddChange { lockedDown = ld }) = 
+                               ifM (checkaddunlocked c)
+                                       ( case mcache of
+                                               Nothing -> addannexed' cfg c True Nothing
+                                               Just cache ->
+                                                       case M.lookup (inodeCacheToKey ct cache) m of
+                                                               Nothing -> addannexed' cfg c True Nothing
+                                                               Just k -> fastadd c k
+                                       , addannexed' cfg c False mcache
+                                       )
+
+       checkaddunlocked (InProcessAddChange { lockedDown = ld }) = 
+               case addunlockedmatcher of
+                       Just addunlockedmatcher' -> do
+                               let mi = MatchingFile $ FileInfo
+                                       { contentFile = contentLocation (keySource ld)
+                                       , matchFile = keyFilename (keySource ld)
+                                       , matchKey = Nothing
+                                       }
+                               liftAnnex $ addUnlocked addunlockedmatcher' mi True
+                       Nothing -> return True
+       checkaddunlocked _ = return True
+
+       addannexed' :: LockDownConfig -> Change -> Bool -> Maybe InodeCache -> Assistant (Maybe Change)
+       addannexed' lockdownconfig change@(InProcessAddChange { lockedDown = ld }) addunlocked mcache = 
                catchDefaultIO Nothing <~> doadd
          where
                ks = keySource ld
@@ -376,14 +399,14 @@ handleAdds lockdowndir havelsof largefilematcher annexdotfiles delayadd cs = ret
                        (mkey, _mcache) <- liftAnnex $ do
                                showStartMessage (StartMessage "add" (ActionItemOther (Just (QuotedPath (keyFilename ks)))) (SeekInput []))
                                ingest nullMeterUpdate (Just $ LockedDown lockdownconfig ks) Nothing
-                       maybe (failedingest change) (done change $ keyFilename ks) mkey
-       addannexed' _ _ = return Nothing
+                       maybe (failedingest change) (done change addunlocked mcache $ keyFilename ks) mkey
+       addannexed' _ _ _ _ = return Nothing
 
        fastadd :: Change -> Key -> Assistant (Maybe Change)
        fastadd change key = do
                let source = keySource $ lockedDown change
                liftAnnex $ finishIngestUnlocked key source
-               done change (keyFilename source) key
+               done change True Nothing (keyFilename source) key
 
        removedKeysMap :: InodeComparisonType -> [Change] -> Annex (M.Map InodeCacheKey Key)
        removedKeysMap ct l = do
@@ -399,11 +422,14 @@ handleAdds lockdowndir havelsof largefilematcher annexdotfiles delayadd cs = ret
                liftAnnex showEndFail
                return Nothing
 
-       done change file key = liftAnnex $ do
+       done change addunlocked mcache file key = liftAnnex $ do
                logStatus NoLiveUpdate key InfoPresent
-               mode <- liftIO $ catchMaybeIO $
-                       fileMode <$> R.getFileStatus (fromOsPath file)
-               stagePointerFile file mode =<< hashPointerFile key
+               if addunlocked
+                       then do
+                               mode <- liftIO $ catchMaybeIO $
+                                       fileMode <$> R.getFileStatus (fromOsPath file)
+                               stagePointerFile file mode =<< hashPointerFile key
+                       else addSymlink file key mcache
                showEndOk
                return $ Just $ finishedChange change key
 
index 740253853a346ce896b1b01963b26906362c8912..72e4a419f5eefa20673c109b62bb6da709b8a98e 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -17,6 +17,7 @@ git-annex (10.20250829) UNRELEASED; urgency=medium
   * Removed support for building with cryptonite, use crypton.
   * p2phttp: Fix a hang that could occur when used with --directory,
     and a repository in the repository got removed.
+  * Added annex.assistant.allowunlocked config.
 
  -- Joey Hess <id@joeyh.name>  Fri, 29 Aug 2025 12:34:06 -0400
 
index 35b07a50a3fd742ed1dea2e82e3c6f2d54e0fc65..156b88c32c7cea3005d03ee98a8b2d553e78fce5 100644 (file)
@@ -157,6 +157,7 @@ data GitConfig = GitConfig
        , annexSkipUnknown :: Bool
        , annexAdjustedBranchRefresh :: Integer
        , annexSupportUnlocked :: Bool
+       , annexAssistantAllowUnlocked :: Bool
        , coreSymlinks :: Bool
        , coreSharedRepository :: SharedRepository
        , coreQuotePath :: QuotePath
@@ -281,6 +282,7 @@ extractGitConfig configsource r = GitConfig
                (if getbool "adjustedbranchrefresh" False then 1 else 0)
                (getmayberead (annexConfig "adjustedbranchrefresh"))
        , annexSupportUnlocked = getbool (annexConfig "supportunlocked") True
+       , annexAssistantAllowUnlocked = getbool (annexConfig "assistant.allowunlocked") False
        , coreSymlinks = getbool "core.symlinks" True
        , coreSharedRepository = getSharedRepository r
        , coreQuotePath = QuotePath (getbool "core.quotepath" True)
index 6b668f69b50f48efd9bb06f433d0c68ca20b9ced..4f633d7f0e2e44f8f16736092f9b2f1204aa14ab 100644 (file)
@@ -1044,13 +1044,23 @@ repository, using [[git-annex-config]]. See its man page for a list.)
   To configure a default annex.addunlocked for all clones of the repository,
   this can be set in [[git-annex-config]](1).
   
-  (Using `git add` always adds files in unlocked form and it is not
-  affected by this setting.)
+  Using `git add` always adds files in unlocked form and it is not
+  affected by this setting. The assistant defaults to adding all files
+  unlocked, unless `annex.assistant.allowunlocked` is set.
   
   When a repository has core.symlinks set to false, or has an adjusted 
   unlocked branch checked out, this setting is ignored, and files are 
   always added to the repository in unlocked form.
 
+* `annex.assistant.allowunlocked`
+
+  The `git-annex assistant` defaults to adding all files unlocked, so that
+  files can be modified without the user needing to do anything to unlock
+  them.
+
+  If this is set to `true` then it will instead use the `annex.addunlocked`
+  configuration to decide which files to add unlocked.
+
 * `annex.numcopies`
 
   This is a deprecated setting. You should instead use the
index ed0a60d5a467e913448070f90df84d4101dce222..2232354158f04e7e1ca214c95d28fee0a676de3d 100644 (file)
@@ -4,11 +4,10 @@ configured to add them locked.
 There are good reasons for that different behavior to be the default,
 but it would be worth having a config to override that.
 
-Eg, when annex.assistant.honor.addunlocked is set, honor the
-annex.addunlocked configuration, which defaults to adding files locked.
-Or perhaps a better name would be annex.assistant.allowaddlocked.
-
 See here for some motivating use cases
 <https://git-annex.branchable.com/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/>
 
 [[!tag projects/repronim]]
+
+> [[done]], git config annex.assistant.allowunlocked to true,
+> and then configure annex.addunlocked as desired. --[[Joey]]